package problem.ej1;

import instance.InstanceEj1;

import java.util.LinkedList;

import output.Problem3Output;
import problem.ej1.Elevator;
import test.TestEj1;
import test.TestEj3;

public class Problem1 {
	public static long contador_ej1;

	// Ascensor.
	Elevator elevator;

	// Cantidad de energ�a disponible para subir pisos.
	private int energyUp;

	// Pisos alcanzables por el ascensor.
	private int[] reachableFloors;
	
	private int maximoDePersonas;
	private int maximoEnUnPiso;

	public Problem1(int energy, int elevatorCapacity, int[] floors) {

		elevator = new Elevator(energy, elevatorCapacity);
		contador_ej1++;
		// Solo se podr� usar una cantidad de energia igual a la mitad
		// de energia disponible para subir.
		energyUp = (energy / 2);
		contador_ej1++;

		// Si hay mas pisos que energia disponible para subir, entonces
		// no podr� alcanzar todos los pisos.

		contador_ej1++;
		if(energyUp == 0)
		{
			contador_ej1++;
			reachableFloors = null;
		}
		else
		{
			contador_ej1++;
			reachableFloors = new int[energyUp];
			contador_ej1+=energyUp;
			contador_ej1++;
			if (floors.length > energyUp)
			{
				contador_ej1++;
				for (int i = 0; i < energyUp; i++) //O(energia/2)
				{
					contador_ej1++;
					reachableFloors[i] = floors[i];
				}
			}
			else
			{
				contador_ej1++;
				reachableFloors = floors;
			}
			// Calculo la cantidad maxima de personas en un piso
			// Calculo la suma maxima de personas en todo el edificio (por lo menos a donde puedo llegar con la energia)
			int maximo = 0;
			int acumulado = 0;
			contador_ej1+=2;
			for (int i = 0; i < reachableFloors.length; i++) //O(pisos)
			{
				contador_ej1++;
				acumulado += reachableFloors[i];
				contador_ej1++;
					if (maximo <= reachableFloors[i])
					{
						contador_ej1++;
						maximo = reachableFloors[i];
					}
			}
				maximoEnUnPiso = maximo;
				maximoDePersonas = acumulado;
				contador_ej1+=2;
			
			}
	}

	// Calcula la cantidad m�xima de gente a bajar.
	public int calculateMaxPeople()
	{
		int min = 0;
		int max = maximoDePersonas;
		contador_ej1+=3;
		
		return maxPeople(min,max);
		
	}

	private int maxPeople(int a, int b) {	
		int resultado = 0;
		int minPosibleSolution = new Integer(a);
		int maxPosibleSolution = new Integer(b);
		int mediumPosibleSolution = (minPosibleSolution + maxPosibleSolution)/2;
		contador_ej1+=4;
		contador_ej1++;
		if (reachableFloors == null)
			// si no tengo pisos a donde ir...
		{
			contador_ej1++;
			return 0;
		}
		else
		{		
			contador_ej1++;
			if (minPosibleSolution < maxPosibleSolution - 1)
			{
				contador_ej1++;
					
				if (recheableCantPeople(mediumPosibleSolution))
				{
					contador_ej1++;
					return maxPeople(mediumPosibleSolution, maxPosibleSolution);
				}
				else
				{contador_ej1++;
				contador_ej1++;
					return maxPeople(minPosibleSolution, mediumPosibleSolution);
				}
			}
			contador_ej1++;
			if(recheableCantPeople(maxPosibleSolution))
			{
				contador_ej1++;
				resultado = maxPosibleSolution;
			}
			else
			{
				contador_ej1++;
				resultado = minPosibleSolution;
			}
		}
		contador_ej1++;
		return resultado;
	}

	private boolean recheableCantPeople(int cantidad) {
		int acumuladoAbajo = 0;
		int acumuladoArriba = 0;
		int i = 0;
		int [] lista = reachableFloors.clone();
		contador_ej1+=3;
		contador_ej1+=lista.length;
		int peopleInElevator = 0;
		int energiaDisponible = new Integer((elevator.getElevatorEnergy()/2)); // esto es porque me lo devuelve por referencia 
		// mientras no me haya pasado de la cantidad o haya llegado a ocupar toda la energia.
		
		contador_ej1+=2;
		boolean Arriba = GoUp(acumuladoAbajo, i, lista, peopleInElevator, energiaDisponible, cantidad);
		contador_ej1++;
			
		// ahora busco desde lo mas arriba y voy bajando
		peopleInElevator = 0;
		contador_ej1++;
		lista = reachableFloors.clone();
		contador_ej1+=lista.length;
		i = lista.length;
		energiaDisponible = new Integer((elevator.getElevatorEnergy()/2));
		contador_ej1++;
		
		contador_ej1++;
		if (lista.length < energiaDisponible)
		{
			contador_ej1++;
			i = lista.length - 1;
		}
		else
		{
			contador_ej1++;
			i = energiaDisponible - 1;
			contador_ej1++;
		}
		
		contador_ej1++;
		boolean Abajo = GoDown(acumuladoArriba, lista, peopleInElevator, i, energiaDisponible, cantidad);
		contador_ej1++;
		return Abajo || Arriba ;
	}

	private boolean GoDown(int acumuladoArriba, int[] lista,
			int peopleInElevator, int i, int energiaDisponible, int cantidad) {
		
		while (acumuladoArriba < cantidad && i >= 0) // O(min(energia,pisos))
		{
			contador_ej1++;
			// si donde estoy parado es mas grande que mi capacidad..
			if( lista[i] >= elevator.getElevatorCapacity())
			{
				contador_ej1++;
				if (acumuladoArriba + (elevator.getElevatorCapacity() - peopleInElevator) > cantidad) // si lo que voy a buscar se pasa de lo que quiero...
				{
					contador_ej1++;
					lista[i] -= cantidad - acumuladoArriba;
					peopleInElevator += cantidad - acumuladoArriba;
					acumuladoArriba += cantidad - acumuladoArriba;
					contador_ej1+=3;
				}
				else
				{
					contador_ej1++;
					// agarro la capacidad del ascensor
					lista[i] -= (elevator.getElevatorCapacity() - peopleInElevator);
					contador_ej1++;
					// el acumulado es lo que puedo bajar
					acumuladoArriba += (elevator.getElevatorCapacity() - peopleInElevator);
					contador_ej1++;
					// ahora en el ascensor tengo a toda la gente.
					peopleInElevator += (elevator.getElevatorCapacity() - peopleInElevator);
					contador_ej1++;
				}
			}
			else
			{// Si lo que hay en ese piso es menor			
				contador_ej1++;
				if (acumuladoArriba + lista[i] > cantidad) // si lo que voy a buscar se pasa de lo que quiero...
				{
					lista[i] -= cantidad - lista[i];
					peopleInElevator += lista[i];
					acumuladoArriba = cantidad;
					contador_ej1+=3;
				}
				else
				{
					contador_ej1++;
					// acumulado es lo que tenia mas lo que agarre ahora.
					acumuladoArriba += lista[i];
					peopleInElevator += lista[i];
					// actualizo el piso
					lista[i] -= lista[i];
					contador_ej1+=3;
				}
			}				
			if (peopleInElevator == elevator.getElevatorCapacity())
			{
				contador_ej1++;
				// tengo que ir a planta baja, descargar la gente, restar esa cantidad de energia que gaste en hacer ese viaje y retomar el resto.
				peopleInElevator = 0;
				energiaDisponible -= i+1;
				
				// i me queda como la energia que me queda
				if (energiaDisponible - 1 > lista.length)
				{
					i--; 
				}
				else
				{
					i = energiaDisponible - 1;
				}

				contador_ej1+=4;
			}
			else
			{
			// resto para seguir buscando.
			i--;
			contador_ej1++;
			}
		}
		contador_ej1++;
		return (acumuladoArriba == cantidad);
	}

	private boolean GoUp(int acumuladoAbajo, int i, int[] lista,
			int peopleInElevator, int energiaDisponible, int cantidad) {
		contador_ej1++;
		while (acumuladoAbajo < cantidad && i < energiaDisponible) // O(energia)
		{
			// si donde estoy parado es mas grande que mi capacidad..
			contador_ej1++;
			if( lista[i] >= elevator.getElevatorCapacity())
			{
				contador_ej1++;
				if (acumuladoAbajo + (elevator.getElevatorCapacity() - peopleInElevator) > cantidad) // si lo que voy a buscar se pasa de lo que quiero...
				{
					contador_ej1++;
					lista[i] -= cantidad - acumuladoAbajo;
					peopleInElevator = cantidad - acumuladoAbajo;
					acumuladoAbajo = cantidad;
					contador_ej1+=2;
				}
				else
				{
					contador_ej1++;
					// agarro la capacidad del ascensor
					lista[i] -= (elevator.getElevatorCapacity() - peopleInElevator);
					// el acumulado es lo que puedo bajar
					acumuladoAbajo += (elevator.getElevatorCapacity() - peopleInElevator);
					// ahora en el ascensor tengo a toda la gente.
					peopleInElevator += (elevator.getElevatorCapacity() - peopleInElevator);
					contador_ej1+=4;
				}
			}
			else
			{// Si lo que hay en ese piso es menor
				contador_ej1++;
				// actualizo el piso
				contador_ej1++;
				if (acumuladoAbajo + lista[i] > cantidad) // si lo que voy a buscar se pasa de lo que quiero...
				{
					contador_ej1+=3;
					lista[i] -= cantidad - lista[i];
					peopleInElevator += lista[i];
					acumuladoAbajo = cantidad;
				}
				else
				{contador_ej1++;
				contador_ej1+=3;
					acumuladoAbajo += lista[i];
					peopleInElevator += lista[i];
					lista[i] -= lista[i];
					// acumulado es lo que tenia mas lo que agarre ahora.
				}
			}
			contador_ej1++;
			if (peopleInElevator == elevator.getElevatorCapacity())
			{
				contador_ej1++;
				contador_ej1+=3;
				// tengo que ir a planta baja, descargar la gente, restar esa cantidad de energia que gaste en hacer ese viaje y retomar el resto.
				peopleInElevator = 0;
				energiaDisponible -= i+1;
				i = 0;
			}
			else
			{
				contador_ej1++;
			// sumo para seguir buscando.
			i++;
			}
		}
		return (acumuladoAbajo == cantidad);
	}
	public static void main(String[] args) throws Exception{
		LinkedList<InstanceEj1>  instancia = TestEj1.generarListaInstanciasAleatorias(500);
		
//		Problem1Output.deListaAArchivo(instancia, "./src/resources/Tp2Ej1.aleatorio.in");
		TestEj1.testRendimiento("./src/resources/Tp2Ej1.aleatorio.in");
	}
		
		
	}
	
